import akshare as ak
import pandas as pd
from datetime import datetime, timedelta
from flask import Flask, jsonify, request, Response
import os
import requests
import json
import re
import numpy as np

app = Flask(__name__)

# Authentication key - in production, store this more securely
# You can change this to your preferred authentication key
AUTH_KEY = "stockdata123"


def get_stock_data(stock_code="601088"):
    """
    获取指定股票代码的实时和历史数据
    """
    # 获取实时行情
    try:
        spot_data = ak.stock_zh_a_spot_em()
        stock_spot = spot_data[spot_data["代码"] == stock_code].iloc[0]
    except Exception as e:
        print(f"获取实时行情出错: {e}")
        stock_spot = {"最新价": "N/A", "涨跌幅": "N/A", "成交量": "N/A", "换手率": "N/A"}

    # 获取2年历史数据（自动计算日期范围）
    end_date = datetime.now().strftime("%Y%m%d")
    start_date = (datetime.now() - timedelta(days=730)).strftime("%Y%m%d")

    try:
        # 尝试不同的股票代码格式
        hist_data = ak.stock_zh_a_hist(
            symbol=stock_code,  # 使用传入的股票代码
            period="daily",
            start_date=start_date,
            end_date=end_date,
            adjust="qfq"
        )

        # 如果返回的数据为空，尝试使用另一个API函数
        if hist_data.empty:
            print("尝试使用备用API获取数据...")
            # 尝试添加前缀
            prefix = "sh" if stock_code.startswith("6") else "sz"
            hist_data = ak.stock_zh_a_daily(
                symbol=f"{prefix}{stock_code}",
                start_date=start_date,
                end_date=end_date,
                adjust="qfq"
            )

        # 标准化列名
        if not hist_data.empty:
            hist_data = hist_data.rename(columns={
                "日期": "date", "开盘": "open", "收盘": "close",
                "最高": "high", "最低": "low", "成交量": "volume"
            })
    except Exception as e:
        print(f"获取历史数据出错: {e}")
        hist_data = pd.DataFrame()

    # 构建股票名称（如果可用）
    stock_name = stock_spot.get('名称', f'股票{stock_code}') if hasattr(stock_spot, 'get') else f'股票{stock_code}'

    # 返回数据
    result = {
        "code": stock_code,
        "name": stock_name,
        "spot_data": {
            "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M'),
            "latest_price": stock_spot['最新价'] if '最新价' in stock_spot else "N/A",
            "change_percent": stock_spot['涨跌幅'] if '涨跌幅' in stock_spot else "N/A",
            "volume": stock_spot['成交量'] if '成交量' in stock_spot else "N/A",
            "turnover_rate": stock_spot['换手率'] if '换手率' in stock_spot else "N/A",
            "main_net_inflow": stock_spot['主力净流入'] if '主力净流入' in stock_spot else "待更新"
        }
    }

    if not hist_data.empty:
        # Convert DataFrame to list of dictionaries for JSON serialization
        result["historical_data"] = hist_data.to_dict(orient="records")
    else:
        result["historical_data"] = []
        result["error"] = "无法获取历史数据，请检查网络连接或API参数"

    return result

def check_auth():
    """检查查询参数中的API密钥认证"""
    api_key = request.args.get('fwkey')
    return api_key and api_key == AUTH_KEY


@app.route('/api/stock', methods=['GET'])
def stock_data():
    """获取股票数据的JSON接口"""
    # 从查询参数获取认证密钥
    if not check_auth():
        return jsonify({"error": "Unauthorized access. Valid 'fwkey' parameter required."}), 401

    try:
        # 从查询参数获取股票代码，默认为601088
        stock_code = request.args.get('code', '601088')
        data = get_stock_data(stock_code)
        return jsonify(data)
    except Exception as e:
        return jsonify({"error": f"Error fetching data: {str(e)}"}), 500


@app.route('/api/stock/text', methods=['GET'])
def stock_data_text():
    """获取股票数据的文本接口"""
    # 从查询参数获取认证密钥
    if not check_auth():
        return Response("Unauthorized access. Valid 'fwkey' parameter required.", mimetype='text/plain'), 401

    try:
        # 从查询参数获取股票代码，默认为601088
        stock_code = request.args.get('code', '601088')
        data = get_stock_data(stock_code)
        spot_data = data["spot_data"]

        # 转换为字符串格式
        spot_str = f"""
[{data.get('name', f'股票{stock_code}')}({stock_code})实时数据] 时间:{spot_data['timestamp']}
最新价:{spot_data['latest_price']} | 涨跌幅:{spot_data['change_percent']}
成交量:{spot_data['volume']}股 | 换手率:{spot_data['turnover_rate']}
主力净流入:{spot_data['main_net_inflow']}
        """.strip()

        if not data["historical_data"]:
            hist_str = "\n\n[历史数据]\n无法获取历史数据，请检查网络连接或API参数"
        else:
            # Convert back to DataFrame for text display
            hist_df = pd.DataFrame(data["historical_data"])
            hist_str = "\n\n[历史数据]\n" + hist_df.to_string(index=False, justify='center')

        return Response(spot_str + hist_str, mimetype='text/plain')
    except Exception as e:
        return Response(f"Error fetching data: {str(e)}", mimetype='text/plain'), 500

# 下面为获取布伦特原油数据的函数和API接口

def get_brent_oil_data():
    # Get current date
    current_date = datetime.now()  # Changed from datetime.datetime.now()
    date_str = f"{current_date.year}_{current_date.month}_{current_date.day}"

    # API URL
    url = f"https://stock2.finance.sina.com.cn/futures/api/jsonp.php/var%20_OIL{date_str}=/GlobalFuturesService.getGlobalFuturesDailyKLine?symbol=OIL&_={date_str}&source=web"

    try:
        response = requests.get(url)
        if response.status_code == 200:
            # Extract the JSON data from the JSONP response
            content = response.text
            # Use regex to extract the JSON array
            json_match = re.search(r'_OIL\d+_\d+_\d+=\((.*)\);', content)
            if json_match:
                json_data = json.loads(json_match.group(1))
                return json_data
            else:
                print("Failed to parse JSONP response")
                return None
        else:
            print(f"Failed to fetch data: HTTP {response.status_code}")
            return None
    except Exception as e:
        print(f"Error fetching data: {e}")
        return None

def format_as_markdown_table(data, num_rows=20):
    """Format the data as a Markdown table with the most recent entries."""
    if not data:
        return "No data available"
    
    # Get the most recent entries
    recent_data = data[-num_rows:]
    
    # Create table header
    table = "| 日期 | 开盘价 | 最高价 | 最低价 | 收盘价 | 成交量 |\n"
    table += "| ---- | ------ | ------ | ------ | ------ | ------ |\n"
    
    # Add rows in reverse order (most recent first)
    for item in reversed(recent_data):
        date = item['date']
        open_price = item['open']
        high = item['high']
        low = item['low']
        close = item['close']
        volume = item['volume']
        
        table += f"| {date} | {open_price} | {high} | {low} | {close} | {volume} |\n"
    
    return table

def filter_last_2_years(data):
    """Filter data to include only the last 2 years"""
    if not data:
        return []

    # Calculate the date 2 years ago
    today = datetime.now()  # Changed from datetime.datetime.now()
    two_years_ago = today - timedelta(days=365 * 2)  # Changed from datetime.timedelta
    two_years_ago_str = two_years_ago.strftime("%Y-%m-%d")

    # Filter the data to include only entries from the last 2 years
    filtered_data = [item for item in data if item['date'] >= two_years_ago_str]

    return filtered_data

@app.route('/api/stock/brentoil/text', methods=['GET'])
def brent_oil_data_text():
    # Check API key authentication
    if not check_auth():
        return Response("Unauthorized access. Valid 'fwkey' parameter required.", mimetype='text/plain'), 401

    try:
        # Get Brent oil data
        data = get_brent_oil_data()
        
        if data:
            # Filter to get the last 2 years of data
            last_2_years_data = filter_last_2_years(data)
            
            # Format response header
            header = f"## 布伦特原油最近2年数据 (共{len(last_2_years_data)}条记录)\n\n"
            
            # Format the data as a Markdown table
            markdown_table = format_as_markdown_table(last_2_years_data, len(last_2_years_data))
            
            # Return the combined response
            return Response(header + markdown_table, mimetype='text/plain')
        else:
            return Response("Failed to retrieve Brent oil data", mimetype='text/plain'), 500
    except Exception as e:
        return Response(f"Error fetching Brent oil data: {str(e)}", mimetype='text/plain'), 500

# 下面为获取ETF数据和计算相关从超跌指标数据

def fetch_etf_data(symbol, start_date, end_date):
    """
    Fetch ETF historical data from akshare
    """
    try:
        # For ETF funds, we use the fund_etf_hist_em interface
        df = ak.fund_etf_hist_em(symbol=symbol, period="daily",
                                 start_date=start_date, end_date=end_date)
        # Rename columns to standard names
        df.columns = [col.lower() for col in df.columns]
        if 'date' not in df.columns and '日期' in df.columns:
            df = df.rename(columns={'日期': 'date', '开盘': 'open', '收盘': 'close',
                                    '最高': 'high', '最低': 'low', '成交量': 'volume',
                                    '成交额': 'amount', '振幅': 'amplitude',
                                    '涨跌幅': 'change_pct', '涨跌额': 'change_amount'})

        # Convert date to datetime
        df['date'] = pd.to_datetime(df['date'])
        # Sort by date ascending
        df = df.sort_values('date')

        return df
    except Exception as e:
        print(f"Error fetching data: {e}")
        return None


def calculate_bias(df, periods=[34, 50, 70]):
    """
    Calculate BIAS for multiple periods
    BIAS = (Close - MA) / MA * 100%
    """
    for period in periods:
        col_name = f'ma_{period}'
        bias_name = f'bias_{period}'
        df[col_name] = df['close'].rolling(window=period).mean()
        df[bias_name] = (df['close'] - df[col_name]) / df[col_name] * 100
    return df


def calculate_williams_r(df, periods=[34, 50, 70]):
    """
    Calculate Williams %R for multiple periods
    WR = (Highest High - Close) / (Highest High - Lowest Low) * -100
    """
    for period in periods:
        highest_high = df['high'].rolling(window=period).max()
        lowest_low = df['low'].rolling(window=period).min()
        df[f'wr_{period}'] = (highest_high - df['close']) / (highest_high - lowest_low) * (-100)
    return df


def calculate_z_score(df, window=10, ema_period=3):
    """
    Calculate Z-Score momentum indicator
    Z-Score = (Close - Lowest Low) / (Highest High - Lowest Low) * 100
    Smoothed with EMA
    """
    highest_high = df['high'].rolling(window=window).max()
    lowest_low = df['low'].rolling(window=window).min()
    df['z_score'] = (df['close'] - lowest_low) / (highest_high - lowest_low) * 100
    # Apply EMA smoothing
    df['z_score_smoothed'] = df['z_score'].ewm(span=ema_period).mean()
    return df


def calculate_volume_divergence(df):
    """
    Calculate volume divergence metrics
    - Volume comparison to previous lows
    - 5-day average turnover rate
    """
    # Calculate price changes
    df['price_change'] = df['close'].diff()

    # Mark local lows (where price change switches from negative to positive)
    df['is_local_low'] = (df['price_change'].shift(1) < 0) & (df['price_change'] > 0)

    # Calculate 5-day average turnover (using volume as proxy since turnover rate not directly available)
    df['vol_5d_avg'] = df['volume'].rolling(window=5).mean()

    # Calculate relative volume (current volume compared to the volume at previous low)
    df['relative_volume'] = np.nan

    # Find previous low points and compare volumes
    low_points = df[df['is_local_low']].index.tolist()
    for i in range(1, len(low_points)):
        current_idx = low_points[i]
        prev_idx = low_points[i - 1]

        if current_idx > 0 and prev_idx > 0:
            relative_vol = df.loc[current_idx, 'volume'] / df.loc[prev_idx, 'volume'] * 100
            df.loc[current_idx, 'relative_volume'] = relative_vol

    # For analysis we'll consider volume divergence when price makes new low but volume is 30% lower
    df['vol_price_divergence'] = ((df['close'] < df['close'].shift(1)) & 
                                  (df['volume'] < df['volume'].shift(1) * 0.7))

    return df

# 访问的例子：http://127.0.0.1:35000/api/stock/oversold/text?fwkey=stockdata123&code=588000
@app.route('/api/stock/oversold/text', methods=['GET'])
def oversold_data_text():
    # Check API key authentication
    if not check_auth():
        return Response("Unauthorized access. Valid 'fwkey' parameter required.", mimetype='text/plain'), 401

    try:
        # 从查询参数获取股票代码，默认为601088
        stock_code = request.args.get('code', '601088')

        # Define the time period
        end_date = datetime.now().strftime("%Y%m%d")
        start_date = (datetime.now() - timedelta(days=360)).strftime("%Y%m%d")  # Last 12 months

        print(f"获取 ETF {stock_code}  from {start_date} to {end_date}")

        print(f"\n{'='*50}")
        print(f"Processing ETF: {stock_code}")
        
        # Fetch data
        df = fetch_etf_data(stock_code, start_date, end_date)
        
        if df is None or df.empty:
            print(f"Failed to fetch data or empty dataset returned for {stock_code}, skipping...")
        
        print(f"Successfully fetched {len(df)} records for {stock_code}")
        
        # Calculate all indicators
        df = calculate_bias(df)
        df = calculate_williams_r(df)
        df = calculate_z_score(df)
        df = calculate_volume_divergence(df)

        return Response(df.to_string(), mimetype='text/plain')
    except Exception as e:
        return Response(f"Error fetching oversold data: {str(e)}", mimetype='text/plain'), 500


if __name__ == "__main__":
    # 设置默认端口为35000，可通过环境变量覆盖
    port = int(os.environ.get("PORT", 35000))
    # 在开发环境使用debug=True，生产环境应设为False
    app.run(host="0.0.0.0", port=port, debug=True)